#include "main.h"
#include "disk.h"

char whitespace[] = " \t\r\n\v";
char dirsplit[] = "\\/";

int main() {
    char cmd_buf[CMD_NAME_LEN] = {0};
    cmds cmd;

    if(open_disk() == -1) {
        error("open disk error\n");
    }

    // ***********打开并初始化文件系统******************
    init_filesystem();
    printf("Formating finished, good luck and have fun!\n");

    // ***********命令识别与执行******************
    while(getcmd(cmd_buf) >= 0) {
        split_with(whitespace, cmd_buf, &cmd);
        updatecmd(&cmd);
        if(cmd.argc == 0) {
            continue;
        }
        if(strcmp(cmd.argv[0], "ls") == 0) {
            ls(&cmd);
            continue;
        }
        if(strcmp(cmd.argv[0], "mkdir") == 0) {
            mkdir(&cmd);
            continue;
        }
        if(strcmp(cmd.argv[0], "touch") == 0) {
            touch(&cmd);
            continue;
        }
        if(strcmp(cmd.argv[0], "cp") == 0) {
            cp(&cmd);
            continue;
        }
        if(strcmp(cmd.argv[0], "shutdown") == 0) {
            break;
        }
        if(strcmp(cmd.argv[0], "print_01") == 0) {
            char buf[BLOCK_SIZE];
            data_read_block(SUPER_START, buf);
            sp_block* sp_block = (struct super_block*) buf;
            printf("block_map:\n");
            for(int i = 0; i < BLOCK_MAP; i++) {
                printf("%08x ",sp_block->block_map[i]);
            }
            printf("\n");
            printf("inode_map:\n");
            for(int i = 0; i < INODE_MAP; i++) {
                printf("%08x ",sp_block->block_map[i]);
            }
            printf("\n");
            break;
        }
        printf("command error\n");
    }

    // ***********关闭文件系统******************
    close_disk();
    return 0;
}

/**
 * 功能：打开并初始化文件系统
 * 前DATA_START分配给超级快和inode项
 * 在INODE_START的初始位置创建一个inode代表根目录
 **/
void init_filesystem() {
    char buf[BLOCK_SIZE];
    sp_block* sp_block;
    inode_item inode;
    uint32_t inode_point[INODE_POINT] = {0};

    // ******************读取第0个数据块作为超级块******************
    data_read_block(SUPER_START, buf);
    sp_block = (struct super_block*) buf;
    if(sp_block->magic_num != 0 && sp_block->magic_num != DATA_MAGIC) {
        error("There is not fileSystem\n");
    }
    if(sp_block->magic_num != 0) {
        return;
    }

    // ******************初始化super_block******************
    // 超级块和inode所分配数据块DATA_START-1都标记为已分配
    // 分配一个ionde项给根目录
    sp_block->magic_num = DATA_MAGIC;
    sp_block->free_block_count = BLOCK_ALL - DATA_START - 1;
    sp_block->free_inode_count = INODE_ALL - 1;
    sp_block->dir_inode_count = 1;
    mymemset(sp_block->block_map, 0, BLOCK_MAP);
    mymemset(sp_block->inode_map, 0, INODE_MAP);
    for(int i=0; i<DATA_START; i++) {
        set_int32(sp_block->block_map, i);
    }
    set_int32(sp_block->inode_map, 0);
    set_int32(sp_block->block_map, DATA_START);
    data_write_block(SUPER_START, buf);

    // ******************初始化inode分配******************
    mymemset(inode_point, 0, INODE_POINT);
    inode_point[0] = DATA_START;
    make_inode(&inode, 0, F_TYPE_DIR, 1, inode_point);  // 根目录大小为0，指向一个数据块，位置为DATA_START
    put_inode_with_id(0, &inode);
}

/**
 * 功能：执行创建文件夹命令
 **/
void mkdir(cmds* cmd) {
    inode_item last_inode;
    char name[DIR_NAME_LEN];
    uint32_t last_id;
    uint32_t block_point[INODE_POINT];
    inode_item inode;
    uint32_t inode_id;

    mymemset(block_point, 0, INODE_POINT);

    // *********命令长度错误判断*************
    if(cmd->argc == 1) {
        printf("There is no dir name\n");
        return;
    } else if (cmd->argc != 2) {
        printf("mkdir command is too long\n");
        return;
    }

    // *********最后一级文件夹***************
    if(!get_last_inode_name(cmd->argv[1], &last_inode, name, &last_id)) {
        return;
    }

    // *********申请block*********
    if(update_sp_block(ONLY_BLOCK, &block_point[0]) == 0) {
        return ;
    }

    //************ 更新目录项***********
    inode_id = make_new_dir(&last_inode, last_id, name, F_TYPE_DIR);
    if(inode_id == -1) {
        reset_sp_block(ONLY_BLOCK, block_point[0]);
        return;
    }

    // *********创建文件夹*******************
    make_inode(&inode, 0, F_TYPE_DIR, 1, block_point);
    put_inode_with_id(inode_id, &inode);
}

/**
 * 功能：执行创建文件命令
 **/
void touch(cmds* cmd) {
    inode_item last_inode;
    char name[DIR_NAME_LEN];
    uint32_t last_id;
    uint32_t inode_id;
    inode_item inode;
    uint32_t block_point[INODE_POINT];

    mymemset(block_point, 0, INODE_POINT);

    // *********命令长度错误判断*************
    if(cmd->argc == 1) {
        printf("There is no file name\n");
        return;
    } else if (cmd->argc != 2) {
        printf("touch command is too long\n");
        return;
    }

    // *********最后一级文件夹***************
    if(!get_last_inode_name(cmd->argv[1], &last_inode, name, &last_id)) {
        return;
    }

    // *********更新目录项************
    inode_id = make_new_dir(&last_inode, last_id, name, F_TYPE_FILE);

    // *********创建文件**************
    make_inode(&inode, 0, F_TYPE_FILE, 0, block_point);
    put_inode_with_id(inode_id, &inode);
}

/**
 * 功能：打印对应地址的所有项目
 **/
void ls(cmds* cmd) {
    inode_item last_inode;
    uint32_t last_id;

    // *********命令长度错误判断*************
    if (cmd->argc > 2) {
        printf("touch command is too long\n");
        return;
    }

    // *********最后一级文件夹***************
    if(cmd->argc == 1) {
        get_inode_with_id(INODE_ROOT_ID, &last_inode);
        last_id = INODE_ROOT_ID;
    } else if(get_last_inode(cmd->argv[1], &last_inode, &last_id, F_TYPE_DIR) == 0) {
        return;
    }
    
    // ********打印所有目录项***************
    printf(".\n");
    printf("..\n");
    print_inode_dir(last_inode);
}

/**
 * 功能：cp，复制文件或文件夹
 **/
void cp(cmds* cmd) {
    inode_item first_inode;
    uint32_t first_id;
    inode_item second_inode;
    uint32_t second_id;
    inode_item new_inode;
    uint32_t new_id;
    uint32_t block_num;
    char name[DIR_NAME_LEN];

    // *********命令长度错误判断*************
    if(cmd->argc != 3) {
        printf("The command len is error\n");
        return;
    }

    // *********要复制的文件inode**************
    if(get_last_inode(cmd->argv[1], &first_inode, &first_id, F_TYPE_FILE) == 0) {
        return;
    }

    // ********要复制到的位置和名字************
    if(get_last_inode_name(cmd->argv[2], &second_inode, name, &second_id) == 0) {
        return;
    }

    new_id = make_new_dir(&second_inode, second_id, name, F_TYPE_FILE);

    // *********创建文件*******************
    copy_inode(&new_inode, &first_inode);
    for(int i=0; i<INODE_POINT; i++) {
        if(new_inode.block_point[i] == 0) {
            continue;
        }
        if(update_sp_block(ONLY_BLOCK, &block_num) == 0) {
            return;
        }
        copy_block(block_num, new_inode.block_point[i]);
        new_inode.block_point[i] = block_num;
    }
    put_inode_with_id(new_id, &new_inode);
}

/**
 * 功能：读取命令到cmd
 * 输出：当读取成功时，返回0
 *      当重复读取10次，仍没有数据，返回-1
 **/
int getcmd(char* buf) {
    int max_enter = 10;
    memset(buf, 0, CMD_NAME_LEN);
    printf("==> ");

    while(!scanf("%[^\n]", buf)) {
        flush_in();
        max_enter--;
        if(max_enter == 0) {
            return -1;
        } 
        printf("==> ");
    }
    flush_in();
    return 0;
}

/**
 * 功能：更新cmd，删除所有仅含/的项
 **/
void updatecmd(cmds* cmd) {
    uint32_t count = 0;

    for(int i=0; i<cmd->argc; i++) {
        while(strchr(dirsplit, cmd->argv[i][0]) && cmd->argv[i][0]) {
            cmd->argv[i] += 1;
        }
        if(!cmd->argv[i][0]) {
            continue;
        }
        cmd->argv[count] = cmd->argv[i];
        count++;
    }
    cmd->argc = count;
}

/**
 * 功能：打印目录inode下所有的目录项
 **/
void print_inode_dir(inode_item inode) {
    uint32_t block_num;
    char buf[BLOCK_SIZE];
    dir_item* dir;

    if(inode.file_type == F_TYPE_FILE) {
        error("Print_dir_inode for a file\n");
    }
    for(int i=0; i<INODE_POINT; i++) {
        block_num = inode.block_point[i];
        if(block_num == 0) {
            continue;
        }
        data_read_block(block_num, buf);
        for(int j=0; j<DIR_ONE_BLOCK; j++) {
            dir = (struct dir_item*) (buf + DIR_SIZE * j);
            if(dir->valid == 0) {
                continue;
            }
            if(dir->type == F_TYPE_FILE) {
                printf("file: ");
            } else {
                printf("dir: ");
            }
            printf("%s\n", dir->name);
        }
    }
}

/**
 * 功能：解析命令/../last_inode/，得到最后的一层的last_inode和对应相关last_id
 * 输入：命令字符串dir，最后一层last，last_id为last_inode对应id
 * 输出：解析命令错误返回0，正确返回1
 **/
int get_last_inode(char* path, inode_item* last_inode, uint32_t* last_id, uint32_t type) {
    uint32_t block_num;
    char name[DIR_NAME_LEN];
    dir_item dir;

    get_last_inode_name(path, last_inode, name, last_id);
    for(int i=0; i<INODE_POINT; i++) {
        block_num = last_inode->block_point[i];
        if(block_num == 0) {
            continue;
        }
        if(get_dir_with_name_in_block(block_num, name, type, &dir)) {
            get_inode_with_id(dir.inode_id, last_inode);
            *last_id = dir.inode_id;
            return 1;
        }
    }
    printf("The last distence is error\n");
    return 0;
}   

/**
 * 功能：解析命令/../last_dir/name，得到最后的一层的文件夹last_dir和对应相关名字name
 * 输入：命令地址path，最后一层文件夹last，名称name，last_id为last_inode对应id
 * 输出：解析命令错误返回0，正确返回1
 **/
int get_last_inode_name(char* path, inode_item* last_inode, char* name, uint32_t* last_id) {
    dirs dir;
    split_with(dirsplit, path, &dir);
    
    get_inode_with_id(INODE_ROOT_ID, last_inode);
    *last_id = INODE_ROOT_ID;    
    strcpy(name, dir.argv[dir.argc-1]);
    for(int i=0; i<dir.argc-1; i++) {
        if(get_next_dir_inode(last_inode, dir.argv[i], last_id) == 0) {
            return 0;
        }
    }
    return 1;
}

/**
 * 功能：将当前目录inode下，寻找目录名为name的目录，更新inode为name对应inode项
 * 输入：inode待更新目录，name待更新目录对应的目录名，id为inode对应id
 * 返回：更新目录错误返回0，正确返回1
 **/
int get_next_dir_inode(inode_item* inode, char* name, uint32_t* id) {
    dir_item dir;
    if(inode->file_type != F_TYPE_DIR) {
        error("This inode is not dir\n");
        return 0;
    }
    for(int i=0; i<INODE_POINT; i++) {
        if(inode->block_point[i] == 0) continue;
        if(get_dir_with_name_in_block(inode->block_point[i], name, F_TYPE_DIR, &dir)) {
            get_inode_with_id(dir.inode_id, inode);
            *id = dir.inode_id;
            if(inode->file_type == F_TYPE_FILE) {
                error("dir.type is different to inode.type\n");
            }
            return 1;
        }
    }
    printf("There is no %s\n", name);
    return 0;
}

/**
 * 功能：在block_point中寻找名字为name的目录项dir
 * 输入：block_point数据块对应数字，name寻找目录项的名字，type为找到目录项类型，dir目录项结果
 * 输出：寻找失败返回0，成功返回1
 **/
int get_dir_with_name_in_block(uint32_t block_point, char* name, int type, dir_item* dir) {
    char buf[BLOCK_SIZE];
    dir_item* dir_item;

    data_read_block(block_point, buf);
    for(int i=0; i<DIR_ONE_BLOCK; i++) {
        dir_item = (struct dir_item*) (buf+DIR_SIZE*i);
        if(strcmp(dir_item->name, name) == 0 && dir_item->valid == 1 && dir_item->type == type) {
            copy_dir_item(dir, dir_item);
            return 1;
        }
    }
    return 0;
}

/**
 * 功能：返回inode中空闲的dir目录项的数据block_num,和对应偏移量
 * 输入：block_num数据块，block_location偏移量
 * 输出：无空闲返回0，空闲返回1
 **/
int get_free_in_inode(inode_item* inode, uint32_t* block_num, uint32_t* block_location) {
    char buf[BLOCK_SIZE];
    dir_item* dir;
    if(inode->size+DIR_SIZE > BLOCK_SIZE*INODE_POINT) {
        printf("There is no space to build a dir\n");
        return 0;
    }
    if(inode->file_type == F_TYPE_FILE) {
        error("Find free dir in point in a file\n");
    }

    for(int i=0; i<INODE_POINT; i++) {
        *block_num = inode->block_point[i];
        if(*block_num == 0) {
            if(update_sp_block(ONLY_BLOCK, block_num) == 0) {
                return 0;
            }
            *block_location = 0;
            inode->block_point[i] = *block_num;
            inode->link++;
            return 1;
        }
        data_read_block(*block_num, buf);
        for(int j=0; j<DIR_ONE_BLOCK; j++) {
            *block_location = DIR_SIZE*j;
            dir = (struct dir_item*) (buf+*block_location);
            if(dir->valid == 0) {
                return 1;
            }
        }
    }
    error("There maybe something error of the dir_item in inode\n");
}

/**
 * 功能：判断当前文件是否已经在目录中
 * 输入：inode需要判断的目录，name需要判断的名字，type需要判断的名字的类型
 * 输出：已经存在返回1， 不存在返回0
 **/ 
int name_in_inode(inode_item inode, char* name, int type) {
    dir_item dir;
    for(int i=0; i<INODE_POINT; i++) {
        if(inode.block_point[i] == 0) continue;
        if(get_dir_with_name_in_block(inode.block_point[i], name, type, &dir)) {
            return 1;
        }
    }
    return 0;
}

/**
 * 功能：申请inode或block空间
 * 输入：
 *      id：申请的id结果
 * 输出：申请失败返回0，成功返回1
 **/
int update_sp_block(int type, uint32_t* id) {
    char buf[BLOCK_SIZE];
    sp_block* sp_block;

    data_read_block(SUPER_START, buf);
    sp_block = (struct super_block*) buf;
    if(type == ONLY_INODE) {
        *id = get_free(sp_block->inode_map, INODE_MAP);
        if(*id == -1) {
            printf("There is no inode for new dir\n");
            return 0;
        }
        set_int32(sp_block->inode_map, *id);
        sp_block->free_inode_count--;

    } else if(type == ONLY_BLOCK) {
        *id = get_free(sp_block->block_map, BLOCK_MAP);
        if(*id == -1) {
            printf("There is no block for new dir\n");
            return 0;
        }
        set_int32(sp_block->block_map, *id);
        sp_block->free_block_count--;
    }
    data_write_block(SUPER_START, buf);
    return 1;
}

/**
 * 功能：返回inode或block空间
 * 输入：
 *      id：返回的id
 * 输出：申请失败返回0，成功返回1
 **/
int reset_sp_block(int type, uint32_t id) {
    char buf[BLOCK_SIZE];
    sp_block* sp_block;

    data_read_block(SUPER_START, buf);
    sp_block = (struct super_block*) buf;
    if(type == ONLY_INODE) {
        reset_int32(sp_block->inode_map, id);
        sp_block->free_inode_count++;
    } else if(type == ONLY_BLOCK) {
        reset_int32(sp_block->block_map, id);
        sp_block->free_block_count++;
    }
    data_write_block(SUPER_START, buf);
    return 1;
}

/** 
 * 功能：根据id得到对应inode
 **/
void get_inode_with_id(uint32_t id, inode_item* inode) {
    unsigned int block_num = id / INODE_ONE_BLOCK + INODE_START;
    int num = id % INODE_ONE_BLOCK;
    char buf[BLOCK_SIZE];
    inode_item* temp;

    data_read_block(block_num, buf);
    temp = (inode_item*) (buf + num * INODE_SIZE);
    copy_inode(inode, temp);
}

/**
 * 功能：根据id写入对应inode
 **/
void put_inode_with_id(uint32_t id, inode_item* inode) {
    unsigned int block_num = id / INODE_ONE_BLOCK + INODE_START;
    int num = id % INODE_ONE_BLOCK;
    char buf[BLOCK_SIZE];
    inode_item* temp;

    data_read_block(block_num, buf);
    temp = (inode_item*) (buf + num * INODE_SIZE);
    copy_inode(temp, inode);
    data_write_block(block_num, buf);
}

/**
 * 功能：读入数据块
 * 输入：block_num数据块数，buf读入的数据位置
 **/
void data_read_block(unsigned int block_num, char* buf) {
    if(disk_read_block(block_num*2, buf) == -1 || disk_read_block(block_num*2+1, buf+DEVICE_BLOCK_SIZE) == -1) {
        error("disk read error\n");
    }
}

/**
 * 功能：写入数据块
 * 输入：block_num数据块数，buf读入的数据位置
 **/
void data_write_block(unsigned int block_num, char* buf) {
    if(disk_write_block(block_num*2, buf) == -1 || disk_write_block(block_num*2+1, buf+DEVICE_BLOCK_SIZE) == -1) {
        error("disk read error\n");
    }
}

/**
 * 功能：在inode对应的位置创建一个新的文件或文件夹，返回其inode的id
 * 返回：创建成功，返回id，失败返回-1
 **/
uint32_t make_new_dir(inode_item* inode, uint32_t id, char* name, uint32_t type) {
    uint32_t block_num;
    uint32_t block_location;
    uint32_t inode_id;
    char buf[BLOCK_SIZE];
    dir_item* dir;

    // *********判断文件夹是否已存在*************
    if(name_in_inode(*inode, name, type)) {
        printf("There is %s,can't make a new\n", name);
        return -1;
    }

    // *********判断空余目录项位置******************
    if(get_free_in_inode(inode, &block_num, &block_location) == 0) {
        return -1;
    }

    // *********更新超级块***********
    if(update_sp_block(ONLY_INODE, &inode_id) == 0)  {
        return -1;
    }

    // *********写入目录项******************
    data_read_block(block_num, buf);
    dir = (struct dir_item*) (buf + block_location);
    make_dir(dir, inode_id, 1, type, name);
    data_write_block(block_num, buf);

    // *********更新最后一级文件夹大小******************
    inode->size += DIR_SIZE;
    put_inode_with_id(id, inode);

    return inode_id;
}

/**
 * 初始化inode为shuch的参数
 **/
void make_inode(inode_item* inode, uint32_t size, uint16_t file_type, uint16_t link, uint32_t* block_point) {
    inode->size = size;
    inode->file_type = file_type;
    inode->link = link;
    for(int i=0; i<INODE_POINT; i++){
        inode->block_point[i] = block_point[i];
    }
}

/**
 * 初始化dir为输入的参数
 **/
void make_dir(dir_item* dir, uint32_t inode_id, uint16_t valid, uint8_t type, char* name) {
    dir->valid = valid;
    dir->inode_id = inode_id;
    dir->type = type;
    strcpy(dir->name, name);
}

/**
 * 功能：复制Inode项
 **/
void copy_inode(inode_item* to, inode_item* from) {
    to->size = from->size;
    to->link = from->link;
    to->file_type = from->file_type;
    for(int i=0; i<INODE_POINT; i++) {
        to->block_point[i] = from->block_point[i];
    }
}

/**
 * 功能：复制目录项
 **/
void copy_dir_item(dir_item* to, dir_item* from) {
    to->inode_id = from->inode_id;
    to->valid = from->valid;
    to->type = from->type;
    strcpy(to->name, from->name);
}

/**
 * 功能：根据数据块id，复制数据块
 **/
int copy_block(uint32_t to, uint32_t from) {
    char buf[BLOCK_SIZE];

    data_read_block(from, buf);
    data_write_block(to, buf);
}

/**
 * 功能：打印错误，退出
 **/
void error(char error_name[]) {
    printf("%s", error_name);
    exit(-1);
}

/**
 * 功能：对于int32_t数组map的第location位置1
 **/
void set_int32(uint32_t* map, unsigned int location) {
    int i = location/32;
    uint32_t j = 0x80000000 >> (location % 32);
    map[i] = map[i] | j;
}

/**
 * 功能：对于int32_t数组map的第location位置0
 **/
void reset_int32(uint32_t* map, unsigned int location) {
    int i = location/32;
    uint32_t j = 0x80000000 >> (location % 32);
    if(map[i]&j == 0) {
        return;
    }
    map[i] = map[i] ^ j;
}

/**
 * 功能：返回map中第一个空闲的bit所代表的位置
 * 输出：第一个空闲代表的位置，如果没有空闲，返回-1；
 **/
uint32_t get_free(uint32_t* map, int len) {
    for(uint32_t i=0; i<len; i++) {
        if(map[i] == 0xffffffff) {
            continue;
        }
        for(uint8_t j=0; j<32; j++) {
            if(((0x80000000 >> j) & map[i]) == 0) {
                return i*32+j;
            }
        }
    }
    return -1;
}

/**
 * 功能：根据splitchar中的字符对字符串path进行分割，得到结果res，包含切割后指针数组和个数
 **/
void split_with(char* splitchar, char* path, struct split* res) {
    int count = 0;
    int i = 0;
    while(strchr(splitchar, path[i]) && path[i]) {
        i++;
    }

    while(path[i]) {
        res->argv[count] = &path[i];
        count++;
        while(!strchr(splitchar, path[i]) && path[i]) {
            i++;
        }
        if(path[i] == 0) {
            break;
        }
        path[i] = 0;
        i++;
        while(strchr(splitchar, path[i]) && path[i]) {
            i++;
        }
    }
    res->argv[count] = 0;
    res->argc = count;
}

/**
 * 功能：清空缓存区
 */
void flush_in() {
    int ch;
    while((ch = getchar()) != EOF && ch != '\n');
}

/**
 * 功能：数组置0
 **/
void mymemset(uint32_t* dst, int val, int size) {
    for(int i=0; i<size; i++) {
        dst[i] = val;
    }
}